<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>epg测试页面</title>

    <link media="all" type="text/css" href="./css/ui.css?v=2014122229711" rel="stylesheet">
    <link media="all" type="text/css" href="./css/pps.css?v=2014122229711" rel="stylesheet">
    <script src="./js/core.js?v=2014122229711" type="text/javascript"></script>
    <script src="./js/ui.js?v=2014122229711" type="text/javascript"></script>
    <script src="./js/qui.js?v=2014122229711" type="text/javascript"></script>
<body>
<!--<svg id="drawSvg">-->
<!--<rect x="15" y="15" width="40" height="40" fill="red">-->
<!--<set attributeName="fill" to="blue" begin="click"/>-->
<!--</rect>-->
<!--</svg>-->
<div id="panel"></div>
</body>
<script type="text/javascript">
window.AMF = {
    parseObject: function (arraybuffer, offset) {
        var loop = 2;
        var objKey = null;
        var objValue = null;
        var element = {}
        var beforeSize = offset;
        try {
            while (loop > 0) {
                objKey = AMF.parseString(arraybuffer, offset);
                offset += objKey.dataSize
                objValue = AMF.parseValue(arraybuffer, offset);
                offset += objValue.dataSize
                element[objKey.value] = objValue.value;
                loop--;
            }

        } catch (e) {
            console.log("error", e, objKey.value, objValue.value);
        }
        return {value: element, dataSize: offset - beforeSize  }
    },
    parseString: function (arraybuffer, offset) {
        var strLen = arraybuffer.getUint16(offset);
        var value = MathUtil.readStr(arraybuffer, offset + 2, strLen);
        return {
            value: value, dataSize: strLen + 2
        }
    },
    parseBoolean: function (arraybuffer, offset) {
        var value = arraybuffer.getUint8(offset);
        value = value ? true : false;
        return {
            value: value, dataSize: 1
        }
    },
    parseDouble: function (arraybuffer, offset) {
        var value = arraybuffer.getFloat64(offset, false);
        return {
            value: value, dataSize: 8
        }
    },
    parseScriptDataValue: function (arraybuffer, offset) {
        var arrayData = [];
        var beforeSize = offset
        let strictArrayLength = arraybuffer.getUint32(offset, false);
        offset += 4;
        try {
            for (var i = 0; i < strictArrayLength; i++) {
                var val = AMF.parseValue(arraybuffer, offset);
                arrayData.push(val.value);
                offset += val.dataSize;

            }
        } catch (e) {
            console.log("parseScriptDataValue", e);
        }
        return {value: arrayData, dataSize: offset - beforeSize }
    },
    parseValue: function (arraybuffer, offset) {
        var valueType = arraybuffer.getUint8(offset);
        var parseObj = null;
        var objectEnd = false;
        switch (valueType) {
            case 0:// Number(Double) type
                parseObj = this.parseDouble(arraybuffer, offset + 1);
                break;
            case 1: // boolean
                parseObj = this.parseBoolean(arraybuffer, offset + 1)
                //throw new Error("wait to deal");
                break;
            case 2: //String
                parseObj = this.parseString(arraybuffer, offset + 1)
                break;
            case 3://objects
                parseObj = this.parseObject(arraybuffer, offset + 1);
                break;
            case 9:
                parseObj = {value: null};
                objectEnd = true;
                break;
            case 10:   // Strict array type
                // ScriptDataValue[n]. NOTE: according to video_file_format_spec_v10_1.pdf
                parseObj = this.parseScriptDataValue(arraybuffer, offset + 1)
                break;
            default :
                throw new Error("wait to deal value:" + valueType);
                break;
        }
        return { value: parseObj.value, dataSize: parseObj.dataSize + 1     }
    }
}
window.FlvDataSource = (function () {
    var _dataView = null;
    var _offset = 0;

    function bindData(dataView) {
        _dataView = dataView
    }

    function getOffset() {
        return _offset
    }

    function getByteLength() {
        if (_dataView == null) {
            return 0;
        }
        return _dataView.byteLength;
    }

    function readEntity(offset, length) {
        return new DataView(_dataView.buffer.slice(offset, offset + length));
    }

    function fetchEntity(length) {
        var ret = new DataView(_dataView.buffer.slice(_offset, _offset + length));
        _offset += length;
        return ret
    }

    function isReadOver() {
        return _offset >= _dataView.byteLength - 4;
    }

    return {
        offset: getOffset,
        byteLength: getByteLength,
        bindData: bindData,
        fetchEntity: fetchEntity,
        readEntity: readEntity,
        isReadOver: isReadOver
    }
})();
window.LogInfo = {
    decodeInfoArr: []
}
window.MathUtil = {
    showU32: function (dataView, offset) {
        return ( dataView.getUint8(offset) << 24 ) | ( dataView.getUint8(offset + 1) << 16) | ( dataView.getUint8(offset + 2) << 8) | dataView.getUint8(offset + 3)
    },
    showU24: function (dataView, offset) {
        return ( dataView.getUint8(offset) << 16 ) | ( dataView.getUint8(offset + 1) << 8) | dataView.getUint8(offset + 2)
    },
    showU16: function (dataView, offset) {
        return ( dataView.getUint8(offset) << 8 ) | ( dataView.getUint8(offset + 1))
    },
    showU8: function (dataView, offset) {
        return dataView.getUint8(offset)
    },
    readStr: function (dataView, offset, length) {
        return  new TextDecoder().decode(dataView.buffer.slice(offset, offset + length));
    }
}

window.FlvTagDecoder = {
    buildTagEntity: function () {
        var tagEntity = {};
        tagEntity.preTagSize = FlvDataSource.fetchEntity(4).getInt32(0);
        console.log(tagEntity.preTagSize);
        tagEntity.header = {};
        var tagHeader = FlvDataSource.fetchEntity(11);
        var tagValue = tagHeader.getInt8(0)
        var descValue = "";
        switch (tagValue) {
            case 8:
                descValue = "音频"
                break;
            case 9:
                descValue = "视频"
                break;
            case 18:
                descValue = "script data "
                break;
            default :
                descValue = "unknown " + tagValue
                break;
        }
        tagEntity.header.tagValue = tagValue;
        tagEntity.header.dataSize = MathUtil.showU24(tagHeader, 1);
        tagEntity.header.timpstamp = MathUtil.showU24(tagHeader, 4);
        tagEntity.header.timpstamp_ex = MathUtil.showU8(tagHeader, 7);

//        var ts2 = MathUtil.showU8(tagHeader, 4)  ;
//        var ts1 = MathUtil.showU8(tagHeader, 5)  ;
//        var ts0 = MathUtil.showU8(tagHeader, 6)  ;
//        var ts3 = MathUtil.showU8(tagHeader, 7)  ;
//        var timestamp = ts0 | ts1 << 8 | ts2 << 16 | ts3 << 24;
//        tagEntity.header.timpstamp = timestamp;
//        tagEntity.header.timpstamp_ex = ts3;
        tagEntity.header.streamId = MathUtil.showU8(tagHeader, 8);
        var tagData = FlvDataSource.fetchEntity(tagEntity.header.dataSize);

        switch (tagValue) {
            case 8:
                   tagEntity.tagData = this.decodeSoundData(tagEntity, tagData);
                break;
            case 9:
                   tagEntity.tagData = this.decodeVideoData(tagEntity, tagData);
                break;
            case 18:
                    tagEntity.tagData = this.decodeScriptData(tagEntity, tagData);
                break;
            default :
                descValue = "unknown " + tagValue
                break;
        }
        console.log(tagEntity);
        return tagEntity;
    },
    decodeVideoData: function (tagEntity, tagDataView) {
        var tagData = {};
        var offset = 0;
        tagData.frameType = (MathUtil.showU8(tagDataView, offset) & 0xF0) >> 4;
        tagData.videoEncodeType = MathUtil.showU8(tagDataView, offset) & 0x0F;
        offset += 1;
        switch (tagData.frameType) {
            case 1:
                console.log("keyframe ");
                break;
            case 2:
                console.log("inter frame ");
                break;
            case 3 :
                console.log("disposable inter frame ");
                break;
            case 4 :
                console.log("generated keyframe ");
                break;
            case 5:
                console.log("video info/command frame");
                break;
            default :
                break;
        }
        switch (tagData.videoEncodeType) {
            case 1:
                console.log("JPEG （currently unused） ");
                break;
            case 2:
                console.log("Sorenson H.263");
                break;
            case 3 :
                console.log("Screen video ");
                break;
            case 4 :
                console.log("On2 VP6 ");
                break;
            case 5:
                console.log("On2 VP6 with alpha channel");
                break;
            case 6:
                console.log("Screen video version 2");
                break;
            case 7:
                console.log("AVC");
                break;
            default :
                break;
        }
    },
    decodeSoundData: function (tagEntity, tagDataView) {

    },
    decodeScriptData: function (tagEntity, tagDataView) {
        var tagData = {};
        var offset = 0;
        tagData.amfType1 = MathUtil.showU8(tagDataView, offset);
        offset += 1;
        tagData.onMetaDataLength = MathUtil.showU16(tagDataView, offset);
        offset += 2;
        tagData.onMetaData = MathUtil.readStr(tagDataView, offset, 10);
        offset += 10;
        tagData.amfType2 = MathUtil.showU8(tagDataView, offset);
        offset += 1;
        tagData.UI32Value = MathUtil.showU32(tagDataView, offset);
        offset += 4;
        var terminal = 0
        if ((tagDataView.getUint32(tagDataView.byteLength - 4, false) & 0x00FFFFFF) == 9) {
            terminal = 3;
        }
        var loop = tagData.UI32Value;
        var objKey = null;
        var objValue = null;
        var element = {}
        try {
            while (loop > 0) {
                objKey = AMF.parseString(tagDataView, offset);
                offset += objKey.dataSize
                objValue = AMF.parseValue(tagDataView, offset);
                offset += objValue.dataSize
                element[objKey.value] = objValue.value;
                loop--;
            }
        } catch (e) {
            console.log("error", e, objKey.value, objValue.value);
        }
        tagData.onMetaData = element
        return tagData;
    }
}
window.FlvDecoder = {
    entity: null,
    textDecoder: new TextDecoder(),
    decodeHeader: function () {
        var flvInfo = FlvDataSource.fetchEntity(3);
        LogInfo.decodeInfoArr.push("flvInfo:" + this.textDecoder.decode(flvInfo));
        var version = FlvDataSource.fetchEntity(1);
        LogInfo.decodeInfoArr.push("version:" + version.getInt8(0));
        var value = FlvDataSource.fetchEntity(1).getInt8(0);
        LogInfo.decodeInfoArr.push("有音频:" + ((value & 4) == 4));
        LogInfo.decodeInfoArr.push("有视频:" + ((value & 1) == 1));
        var headerSize = FlvDataSource.fetchEntity(4).getInt32(0);
        LogInfo.decodeInfoArr.push("headerSize:" + headerSize);
    },
    decodeBody: function () {
        this.decodeTag();
    },
    // data source = {  }
    decodeTag: function () {
        var tagArr = [];
        var tagEntity = null;
        var tagNo = 1;
        while (!FlvDataSource.isReadOver()) {
            tagEntity = FlvTagDecoder.buildTagEntity();
            tagEntity.tagNo = tagNo;
            tagNo++;
            tagArr.push(tagEntity);
        }
        console.log(JSON.stringify(tagArr) );
    },
    bindData: function (data) {
        this.entity = data;
    },
    readEntity: function (offset, length) {
        return new DataView(this.entity.buffer.slice(offset, offset + length));
    },
    startDecode: function () {
        LogInfo.decodeInfoArr = [];
        this.decodeHeader();
        this.decodeBody();
        console.log(LogInfo.decodeInfoArr);
    },
    startLoad: function () {
        var startTime = Date.now();
        var xhr = new XMLHttpRequest();
        xhr.responseType = "arraybuffer"
       // xhr.open("GET", "./swfs/1506415846-.flv", true);
        xhr.open("GET", "http://10.23.70.35/BJXWGQUDI/flv/1511927926.flv", true);
       // xhr.open("GET", "./swfs/1511164934.flv", true);
         //xhr.open("GET", "./swfs/1511164928.flv", true);
        // xhr.open("GET", "./swfs/1511164934.flv", true);

        //http://10.23.70.35/BJXWGQUDI/flv/1511164928.flv
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                FlvDataSource.bindData(new DataView(xhr.response));
                FlvDecoder.startDecode();
                console.log("cost time" + ( Date.now() - startTime))
            }
        }
        xhr.send();
    },

    startLoad2:function(){
        var requestData = {url: "http://10.23.70.35/BJXWGQUDI/flv/1511927926.flv", type: "get"};
        var startTime = Date.now();
        var newOptions = {
            url: "/api/redirectDownload",
            type: "post",
            dataType:"arraybuffer",
            data: {"request": JSON.stringify(requestData), timeSign: new Date().getTime()},

            success: function (data) {
                console.log("success");
                FlvDataSource.bindData(new DataView(char2buf(data  )));
                FlvDecoder.startDecode();
                console.log("cost time" + ( Date.now() - startTime))
              //  options.success(data);
            },
            error: function (xhr) {
                if (xhr.readyState == 4) {
                    FlvDataSource.bindData(new DataView(char2buf(xhr.responseText) ));
                    FlvDecoder.startDecode();
                    console.log("cost time" + ( Date.now() - startTime))
                }
             //   options.error(data);
            }
        }
        $.ajax(newOptions);
    }
}

function char2buf(str){
    var out = new ArrayBuffer(str.length*2);
    var u16a= new Uint16Array(out);
    var strs = str.split("");
    for(var i =0 ; i<strs.length;i++){
        u16a[i]=strs[i].charCodeAt();
    }
    return out;
}
$(document).ready(function () {
    FlvDecoder.startLoad();
})
</script>
</html>